---
type: playground
order: 201
meta_title: Data Labeling & Annotation Tool Interactive Demo
meta_description: Label Studio interactive demo and playground for data labeling and annotation for machine learning and data science projects.
---
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<style>
  .page-type-playground .content-grid {
    display: block;
  }
  .page-type-playground .container {
    max-width: 110rem;
    padding: 1em 2.5rem;
  }
  .page-type-playground .content-footer {
    max-width: 1030px;
    margin: 4em auto;
  }

  @media (max-width: 1100px) {
    .page-type-playground .content-footer {
      padding-left: 1em;
      padding-right: 1em;
    }
    .page-type-playground .container {
      padding: 1em;
    }
  }
  .content {
     max-width: none !important;
     margin-left: 0 !important;
     padding: 1em 0 0 0;
  }
  
  .validation {
     margin-top: 1em;
     margin-left: 1em;
     color: red;
     text-transform: capitalize;
  }
  
  .CodeMirror {
     min-height: 500px !important;
  }
  
  h1 {
    margin-bottom: 0.5em !important;
  }
  
  h3 {
    margin: 1em 0 !important;
    font-weight: normal;
    width: unset;
    height: unset;
  }
  
  iframe {
     border: 0;
     margin: 0 !important;
     padding: 0 !important;
  }

  #render-editor {
     width: 100%;
  }

  #editor-wrap {
     padding: 0;
     margin: 0;
     display: none;
  }

  .preview {
     padding: 5px;
     overflow: auto;
     white-space: pre;
  }

  .editor-row {
      display: flex;
      margin: 0 auto 1.5em;
      width: 100% !important;
      max-width: 110rem;
  }

  .intro-wrapper{
    background: white;
    border: 1px solid var(--color-gray-300);
    border-radius: .75rem;
    padding: 1.5rem;
    max-width: 110rem;
    margin: 0 auto 1.5rem;
  }

   .data-row {
      display: flex;
      max-width: 110rem;
      margin: 0 auto;
      background: white; 
      border: 1px solid var(--color-gray-300);
      border-radius: .75rem;
      padding: 1.5rem;
   }

  .preview-col {
      width: 60%;
      flex: 1;
      background: white;
      border: 1px solid var(--color-gray-300);
      border-radius: .75rem;
      padding: 1.5rem;
   }

  .editor-area {
      border: 1px solid rgba(34,36,38,.15);
      border-radius: .5rem;
      overflow: hidden;
  }

  .config-col p{
    margin: .75rem 0 0 0;
    color: var(--color-gray-600);
  }

  .config-col {
      margin-right: 1.5em;
      width: 40%;
      background: white;
      border: 1px solid var(--color-gray-300);
      border-radius: .75rem;
      padding: 1.5rem;
  }

  .config-wrapper{
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1rem;
  }

  .config-wrapper h4{
    margin-top: 0;
    margin-bottom: 0;
  }

  .input-col {
      width: 49%;
      padding-right: 2%;
  }

  .output-col {
      width: 49%;
  }
  .hidden {
      display: none !important;
  }

  h4 {
    margin-bottom: 1rem;
    margin-top: 0 !important;
  }

  /* hide title "basic template configs" */
  #basic-templates>.title {
    display: none;
  }
  #adv-templates>.title {
    margin-bottom: 1em;
    cursor: pointer;
  }
  #adv-templates>.content {
    display: none
  }

  .message, .accordion {
    margin-top: .5rem;
  }
  .grid {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: horizontal;
    -webkit-box-direction: normal;
        -ms-flex-direction: row;
            flex-direction: row;
    -ms-flex-wrap: wrap;
        flex-wrap: wrap;
    -webkit-box-align: stretch;
        -ms-flex-align: stretch;
            align-items: stretch;
    padding: 0;
  }

  .loading{
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .loading img{
    border: none;
  }

  .column {
    width: 20% !important;
  }
  .use-template {
    font-weight: normal!important;
    margin-bottom: .5rem;
    display: block;
  }

  @font-face {
    font-family: 'Icons';
    src: url("/fonts/icons.eot");
    src: url("/fonts/icons.eot?#iefix") format('embedded-opentype'), url("/fonts/icons.woff2") format('woff2'), url("/fonts/icons.woff") format('woff'), url("/fonts/icons.ttf") format('truetype'), url("/fonts/icons.svg#icons") format('svg');
    font-style: normal;
    font-weight: normal;
    font-variant: normal;
    text-decoration: inherit;
    text-transform: none;
  }
  i.icon {
    opacity: 0.75;
    display: inline-block;
    margin: 0 0 0 0;
    width: 1.18em;
    height: 1em;
    font-family: 'Icons';
    font-style: normal;
    font-weight: normal;
    text-decoration: inherit;
    text-align: center;
    speak: none;
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
    -webkit-backface-visibility: hidden;
            backface-visibility: hidden;
  }

  i.icon + .ui{
    margin-top: .75rem;
  }

  i.icon:before {
    background: none !important;
  }
  i.icon.sound:before {
    content: "\f025";
  }
  i.icon.image:before {
    content: "\f03e";
  }
  i.icon.code:before {
    content: "\f121";
  }
  i.icon.font:before {
    content: "\f031";
  }
  i.icon.video:before {
    content: "\f03d";
  }
  i.icon.share:before {
    content: "\f064"
  }
  i.icon.copy.outline:before {
    content: "\f0c5"
  }
  i.icon.archive:before {
    content: "\f187";
  }
  i.icon.eye:before {
    content: "\f06e";
  }
  i.icon.bullseye:before {
    content: "\f140";
  }
  i.icon.vector.square:before {
    content: "\f5cb";
  }
  i.icon.wave.square:before {
    content: "\f83e"
  }
  i.icon.dropdown:before {
    content: "\f0da";
  }
  i.icon.dropdown.active:before {
    content: "\f0d7";
  }

  .share-buttons {
    float:right;
    margin: 0 0 0 1em;
  }
  .share-buttons i {
    cursor: pointer;

    color: var(--color-orange-400);
    transition: 0.25s;
  }
  .share-buttons i:hover {
    opacity: 1 !important;
    transition: 0.25s;
  }

  .intro {
    text-align: left;
  }

@media screen and (max-width: 900px) {
  .sidebar {
    display: flex;
  }
}
@media only screen and (max-width: 767.98px) {
    .intro {
      padding-left: 0;
    }
    .grid {
      width: auto;
      margin-left: 0 !important;
      margin-right: 0 !important;
    }
    .column {
      width: 100% !important;
      margin: 0 0 !important;
      -webkit-box-shadow: none !important;
              box-shadow: none !important;
      padding: 1rem 1rem !important;
    }

    .editor-row {
        flex-direction: column;
        max-width: 1300px;
    }
    .data-row {
        flex-direction: column;
    }
    .preview-col {
        width: 100%;
    }
    .config-col {
        width: 100%;
    }
    .input-col, .output-col {
        width: 100%;
    }

}

.playground-cta {
  background-color: var(--color-gray-100);
  border: 1px var(--color-gray-300) solid;
  padding: 2em;
  border-radius: .75rem;
  max-width: 1030px;
  margin: 3em auto -1em auto;
  display: flex;
  align-items: center;
  flex-direction: column;
  position: relative;
  text-align: center;
  overflow: hidden;
}

.playground-cta > svg {
  position: absolute;
  right: 0;
  bottom: 0;
}

.playground-cta .Heading {
  margin: 0;
}

.playground-cta-button-group {
  margin-top: 0.9em;
  display: flex;
  gap: 1em;
  flex-wrap: wrap;
  justify-content: center;
  z-index: 2;
}

.page-tier-enterprise .Secondary:not(:hover) {
  background-color: #fff;
}

</style>

<!-- html -->
<div class="intro-wrapper">
  <div class="intro">
    <h4>1. Choose annotation template</h4>
  </div>

  <%- include('template_titles') %>
</div>

<div>
  <div class="editor-row">
    <div class="config-col">
      <div class="config-wrapper">
        <h4 style="display: inline-block">2. Edit labeling config</h4>
        <span class="share-buttons">
          <i class="icon copy outline" style="cursor: pointer" title="Copy labeling config"></i>
          <i class="icon share" style="cursor: pointer" title="Copy link to this playground"></i>
        </span>
      </div>
      <div class="editor-area">
      <!-- Textarea -->
      <textarea name="label_config" cols="40" rows="10" class="project-form htx-html-editor"
                id="id_label_config"></textarea>
      </div>

      <p>
        Start typing in the config, and you can quickly preview the labeling interface.
        At the bottom of the page, you have live serialization updates
        of what Label Studio expects as an input and what it gives you as a result of your labeling work.
      </p>
    </div>

    <div class="preview-col">
      <h4>3. Inspect interface preview</h4>
      <div class="validation"></div>
      <div id="editor-wrap">
      </div>
      <div class="preview" id="preload-editor">
        <div class="loading" style="margin: 20px; opacity: 0.8">
            <img width="40px" src="/images/design/loading.gif">
            <span style="position: relative; top: -14px">&nbsp;&nbsp;&nbsp;Loading Label Studio, please wait ...</span>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- Preview in two cols -->
<div class="data-row">
  <div class="input-col">
    <h4>Input preview</h4>
    <pre class="preview" id="upload-data-example">...</pre>
  </div>
  <div class="output-col">
    <h4>Output preview</h4>
    <pre class="preview" id="data-results">...</pre>
  </div>
</div>

</div>


<div class="playground-cta">
  <h2 class="Heading Large">Want to put these templates into practice?</h2>
  <div class="playground-cta-button-group">
    <a href="https://labelstud.io/guide/install.html" class="Button Secondary">Install Open Source</a>
    <a href="https://humansignal.com/free-trial" class="Button">Try Enterprise Cloud</a>
  </div>
  <svg width="67" height="68" viewBox="0 0 67 68" fill="none" xmlns="http://www.w3.org/2000/svg">
    <rect x="22.4653" y="45.0219" width="22.2281" height="22.2281" fill="#FF7557"/>
    <rect x="22.4653" y="22.7938" width="22.2281" height="22.2281" fill="#FFA663"/>
    <rect x="44.6936" y="0.565613" width="22.2281" height="22.2281" fill="#E37BD3"/>
    <rect x="44.6936" y="22.7938" width="22.2281" height="22.2281" fill="#FF7557"/>
    <rect x="0.237305" y="22.7938" width="22.2281" height="22.2281" fill="#E37BD3"/>
  </svg>
</div>


<!-- Hidden template codes -->
<empty>
  <%- include('template_start') %>
  <%- include('template_codes') %>
</empty>




<script>

  /*!
 * JavaScript Cookie v2.2.0
 * https://github.com/js-cookie/js-cookie
 *
 * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
 * Released under the MIT license
 */
;(function (factory) {
	var registeredInModuleLoader = false;
	if (typeof define === 'function' && define.amd) {
		define(factory);
		registeredInModuleLoader = true;
	}
	if (typeof exports === 'object') {
		module.exports = factory();
		registeredInModuleLoader = true;
	}
	if (!registeredInModuleLoader) {
		var OldCookies = window.Cookies;
		var api = window.Cookies = factory();
		api.noConflict = function () {
			window.Cookies = OldCookies;
			return api;
		};
	}
}(function () {
	function extend () {
		var i = 0;
		var result = {};
		for (; i < arguments.length; i++) {
			var attributes = arguments[ i ];
			for (var key in attributes) {
				result[key] = attributes[key];
			}
		}
		return result;
	}

	function init (converter) {
		function api (key, value, attributes) {
			var result;
			if (typeof document === 'undefined') {
				return;
			}

			// Write

			if (arguments.length > 1) {
				attributes = extend({
					path: '/'
				}, api.defaults, attributes);

				if (typeof attributes.expires === 'number') {
					var expires = new Date();
					expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
					attributes.expires = expires;
				}

				// We're using "expires" because "max-age" is not supported by IE
				attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';

				try {
					result = JSON.stringify(value);
					if (/^[\{\[]/.test(result)) {
						value = result;
					}
				} catch (e) {}

				if (!converter.write) {
					value = encodeURIComponent(String(value))
						.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
				} else {
					value = converter.write(value, key);
				}

				key = encodeURIComponent(String(key));
				key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
				key = key.replace(/[\(\)]/g, escape);

				var stringifiedAttributes = '';

				for (var attributeName in attributes) {
					if (!attributes[attributeName]) {
						continue;
					}
					stringifiedAttributes += '; ' + attributeName;
					if (attributes[attributeName] === true) {
						continue;
					}
					stringifiedAttributes += '=' + attributes[attributeName];
				}
				return (document.cookie = key + '=' + value + stringifiedAttributes);
			}

			// Read

			if (!key) {
				result = {};
			}

			// To prevent the for loop in the first place assign an empty array
			// in case there are no cookies at all. Also prevents odd result when
			// calling "get()"
			var cookies = document.cookie ? document.cookie.split('; ') : [];
			var rdecode = /(%[0-9A-Z]{2})+/g;
			var i = 0;

			for (; i < cookies.length; i++) {
				var parts = cookies[i].split('=');
				var cookie = parts.slice(1).join('=');

				if (!this.json && cookie.charAt(0) === '"') {
					cookie = cookie.slice(1, -1);
				}

				try {
					var name = parts[0].replace(rdecode, decodeURIComponent);
					cookie = converter.read ?
						converter.read(cookie, name) : converter(cookie, name) ||
						cookie.replace(rdecode, decodeURIComponent);

					if (this.json) {
						try {
							cookie = JSON.parse(cookie);
						} catch (e) {}
					}

					if (key === name) {
						result = cookie;
						break;
					}

					if (!key) {
						result[name] = cookie;
					}
				} catch (e) {}
			}

			return result;
		}

		api.set = api;
		api.get = function (key, default_value) {
			var value = api.call(api, key);
			return value === undefined ? default_value: value;
		};
		api.getJSON = function () {
			return api.apply({
				json: true
			}, [].slice.call(arguments));
		};
		api.defaults = {};

		api.remove = function (key, attributes) {
			api(key, '', extend(attributes, {
				expires: -1
			}));
		};

		api.withConverter = init;

		return api;
	}

	return init(function () {});
}));


  // copy to clipboard
  var copyToClipboard = function (str) {
    var el = document.createElement('textarea');  // Create a <textarea> element
    el.value = str;                                 // Set its value to the string that you want copied
    el.setAttribute('readonly', '');                // Make it readonly to be tamper-proof
    el.style.position = 'absolute';
    el.style.left = '-9999px';                      // Move outside the screen to make it invisible
    document.body.appendChild(el);                  // Append the <textarea> element to the HTML document
    var selected =
      document.getSelection().rangeCount > 0        // Check if there is any content selected previously
        ? document.getSelection().getRangeAt(0)     // Store selection if found
        : false;                                    // Mark as false to know no selection existed before
    el.select();                                    // Select the <textarea> content
    document.execCommand('copy');                   // Copy - only works as a result of a user action (e.g. click events)
    document.body.removeChild(el);                  // Remove the <textarea> element
    if (selected) {                                 // If a selection existed before copying
      document.getSelection().removeAllRanges();    // Unselect everything on the HTML document
      document.getSelection().addRange(selected);   // Restore the original selection
    }
  };

  function uuidv4() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
    });
  }

  var confirm_already_shown = true;
  var edit_count = 0;
  var current_template_name = 'start';
  var current_template_category = 'start';
  var page_hash = uuidv4();
  var user_hash = Cookies.get('user_hash');
  if (user_hash === "null" || !user_hash) {
      user_hash = uuidv4();
      Cookies.set('user_hash', user_hash);
  }
  var lookup = {};

  $.ajax({
      url: 'https://extreme-ip-lookup.com/json/',
      success: function(o) { lookup = o },
      async: false
  });

  $(function () {

    function addTemplateConfig($el) {
      var template_pk = $el.data('value');
      var value = $('[data-template-pk="' + template_pk + '"]').html();

      // extract readme from config
      var starter = '<!-- readme', terminator = '-->';
      var start = value.indexOf(starter);
      if (start >= 0) {
        var body_length = value.indexOf(terminator, start) - start - starter.length;
        var readme = value.substr(start + starter.length, body_length);

        // find first XML tag (<View> as usual) and start from it
        value = value.slice(value.indexOf('<', start + starter.length + body_length + terminator.length))
      }

      labelEditor.setValue(value);
      first_render = true;
    }

    $('.use-template').on('click', function () {
      var $el = $(this);
      edit_count = 0;
      current_template_name = $el.text();
      current_template_category = $($el.parent().parent().find('i')[0]).attr('title');

      if (labelEditor.getValue() !== '' && !confirm_already_shown) {
        var dialog = $('#confirm-config-template-dialog');
        dialog.modal({
          closable: true,
          keyboardShortcuts: true,
          onApprove: function () {
            addTemplateConfig($el);
          }
        }).modal('show');

        // close on enter, unfortunately keyboardShortcuts doesn't work
        dialog.on('keypress', function () {
          if (event.keyCode === 13) {
            dialog.modal('hide');
            addTemplateConfig($el);
          }
        });

        confirm_already_shown = true;

      } else {
        addTemplateConfig($el);
      }

      return false;
    });

    var iframeTimer = null;

    function debounce(func, wait, immediate) {
      let timeout;

      return function () {
        const context = this, args = arguments;
        const later = () => {
          timeout = null;
          if (!immediate) func.apply(context, args);
        };
        const callNow = immediate && !timeout;

        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
      };
    }

    var prev_completion = null;

    // serialize editor output by timer
    setInterval(function () {
      let iframe = document.getElementById('render-editor');
      if (iframe !== null) {
        let Htx = iframe.contentWindow.Htx;
        if (typeof Htx !== 'undefined') {
          var completion = JSON.stringify(Htx.completionStore.selected.serializeCompletion(), null, 4);
          if (prev_completion !== completion) {
            if (completion.length > 3000) {
              completion = completion.slice(0, 3000) + ' ...';
            }
            $('#data-results').text(completion);
            prev_completion = completion;
          }
        }
      }
    }, 500);


    var host = "https://app.heartex.ai";
    var url_string = window.location.href;
    var url = new URL(url_string);

    // Label code mirror
    var labelEditor = CodeMirror.fromTextArea(document.getElementById('id_label_config'), {
      lineNumbers: true,
      mode: "text/html"
    });
    labelEditor.focus();

    var _c = url.searchParams.get("config");
    if (_c && _c.length > 0) {
      var config = url.searchParams.get("config");
      config = config.replace(/[<][b][r][>]/gm, "\n");
      labelEditor.setValue(config);
    } else {
      labelEditor.setValue($('#start-template').html());
    }
    validate_config(labelEditor);

    // refresh for proper line numbers drawing
    labelEditor.refresh();
    // add validation
    labelEditor.on('change', debounce(function (editor) {
      validate_config(editor);
    }, 500));

    window.labelEditor = labelEditor;

    function validate_name() {
      let name = $('#id_title').val();
      validation_message('', 0);
      return 0;
    }

    function validation_message(msg, status) {
      let o = $('.validation');
      o.text(msg);

      if (status === -1) {
        o.removeClass('hidden');
        o.addClass('visible');
      }
      if (status === 0) {
        o.removeClass('visible');
        o.addClass('hidden');
      }
    }

    // storage of validation results
    // let is_collection_ok = false;
    let is_label_ok = false;

    function editor_iframe(res) {
      // generate new iframe
      let iframe = $('<iframe><iframe>');
      iframe.className = "editor-preview";
      // add iframe to wrapper div
      $('#editor-wrap').html(iframe);
      $('#editor-wrap').fadeIn();

      iframe.on('load', function () {
        // remove old iframe
        $('#render-editor').hide();
        $('#render-editor').remove();
        // assign id to new iframe
        iframe.attr('id', 'render-editor');
        // force to hide undo / redo / reset buttons
        $('#render-editor').contents().find('head').append('<style>.ls-panel{display:none;}' +
          '.ls-editor { margin-left: 3px!important;}</style>');
        iframe.show();
        let obj = document.getElementById('render-editor');

        // wait until all images and resources from iframe loading
        clearTimeout(iframeTimer);
        iframeTimer = setInterval(function () {
          if (obj.contentWindow) {
            obj.style.height = (obj.contentWindow.document.body.scrollHeight) + 'px';
          }
        }, 100);
        // hide "..."
        $('#preload-editor').hide();
      });

      // load new data into iframe
      iframe.attr('srcdoc', res);
    }

    function show_render_editor(editor) {
      let config = labelEditor.getValue();
      edit_count++;
      $.ajax({
        url: host + '/demo/render-editor?full_editor=t&playground=1',
        method: 'POST',
        xhrFields: { withCredentials: true },
        data: {
            config: config,
            lookup: lookup,
            page_hash: page_hash,
            user_hash: user_hash,
            current_template_name : current_template_name,
            current_template_category: current_template_category,
            edit_count: edit_count
        },
        success: editor_iframe,
        error: function () {
          $('#preload-editor').show();
        }
      })
    }

    // send request to server with configs to validate
    function validate_config(editor) {

      // get current scheme type from current editor
      let url = host + '/api/projects/validate/';
      let val = labelEditor.getValue();

      if (!val.length)
        return;

      // label config validation
      $.ajax({
        url: url,
        method: 'POST',
        data: {label_config: val},
        success: function (res) {
          is_label_ok = true;
          validation_message('', 0);
          $('#render-editor').show();
          show_render_editor(editor);
          // check_submit_button();
        },
        error: function (res) {
          is_label_ok = false;
          validation_message(res.responseJSON['label_config'][0], -1);
          $('#render-editor').hide();
          // check_submit_button();
        }
      });

      // load sample task
      $.post({
        url: host + '/business/projects/upload-example/?playground=1',
        data: {label_config: val}
      })
        .fail(function(o) {
          $('#upload-data-example').text('...')
        })
        .done(function(o) {
          $('#upload-data-example').text(JSON.stringify(JSON.parse(o), null, 4))
        })
    }

    $('.share-buttons .copy').on('click', function() {
       copyToClipboard(labelEditor.getValue());
        $(event.target).css('color', 'green');
    });

    $('.share-buttons .share').on('click', function() {
        let config = labelEditor.getValue();
        config = encodeURIComponent(config.replace(/(\r\n|\n|\r)/gm, "<br>"));
        let link = window.location.origin + window.location.pathname + '?config=' + config;
        copyToClipboard(link);
        $(event.target).css('color', 'green');
    });


  });
</script>
